/*
 * rtops.c
 *  RealTime operations: Scheduler operations.
 *
 * To see if the app is running in RT use:
 *   ps -eLo pid,cls,rtprio,pri,nice,cmd
 *
 *
 *  Created on: Dec 3, 2010
 */

#ifdef __sun

#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/rtpriocntl.h>
char f_szFixedPrioritySchedulerName[] = "RT";

#else

#include <time.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>

char f_szFixedPrioritySchedulerName[] = "SCHED_FIFO";

#endif

#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>

#include "rtops.h"

/**
 *
 id_t Class id. policy on Linux.
 char *name: Name of the scheduler being investigated.
 Solaris: TS, RT.
 Linux: SCHED_OTHER, SCHED_RR.
 short *maxpri: Maximum priority for 'name' scheduler.
 */
id_t GetSchedulerInfo(char *name, short *maxpri) {
	id_t nSchedClassId;

#ifdef __sun
	pcinfo_t info;
	tsinfo_t *tsinfop;
	rtinfo_t *rtinfop;

	(void) strcpy(info.pc_clname, name);
	if (priocntl (0L, 0L, PC_GETCID, &info) == -1L) {
		return (-1);
	}
#endif
	if (strcmp(name, "TS") == 0) {
#ifdef __sun
		tsinfop = (struct tsinfo *) info.pc_clinfo;
		*maxpri = tsinfop->ts_maxupri;
		nSchedClassId = info.pc_cid;
#else
		*maxpri = sched_get_priority_max(SCHED_OTHER);
		nSchedClassId = SCHED_OTHER;
#endif
	} else if (strcmp(name, f_szFixedPrioritySchedulerName) == 0) {
#ifdef __sun
		rtinfop = (struct rtinfo *) info.pc_clinfo;
		*maxpri = rtinfop->rt_maxpri;
		nSchedClassId = info.pc_cid;
#else
		// TODO V Fix this, to work properly.
		*maxpri = sched_get_priority_max(SCHED_RR);
		nSchedClassId = SCHED_RR;
#endif
	} else {
		return (-1);
	}
	return (nSchedClassId);
} // end getschedulerinfo




/**
 * Sets the scheduling class and priority for the given PID.
 *
 * @param nPriority: Priority within the Scheduling class.
 * @param nPid: The PID for the task to get the scheduler change.
 *         pid=0 means current Taks.
 * @return 0 on success.
 * Please note that you need to have root priviliges to change
 *  the scheduler class.
 */
int SetRtPriority(int nPriority, pid_t nPid) {
	id_t rtID;
	short maxrtpri;
	int nRc = -1;
	int nNewPriority = nPriority;
	struct sched_param stSchedulerParm = { .sched_priority = 1, };

	// TODO V Code pre-condition about positive numbers and not using 0 as pid?

	/* Get RT Scheduler class id */
	if ((rtID = GetSchedulerInfo(f_szFixedPrioritySchedulerName, &maxrtpri)) < 0) {
		perror("GetSchedulerInfo failed for realtime class");
	}

	// printf("RT Class id = %d, priority range 0 through %d\n", rtID, maxrtpri);

	if (nPriority > maxrtpri || nPriority < 0) {
		fprintf(
				stderr,
				"RT priority greater that max. priority. Using max. priority = %d\n",
				maxrtpri);
		nNewPriority = maxrtpri;
	} else

#ifdef __sun
		pcparms.pc_cid = rtID;
		rtparmsp = (struct rtparms *) pcparms.pc_clparms;
		rtparmsp->rt_tqnsecs = RT_TQDEF;
		rtparmsp->rt_pri = nNewPriority;
		nRc=priocntl (P_PID, P_MYID, PC_SETPARMS, &pcparms);
#else
		stSchedulerParm.sched_priority = nNewPriority;
	//nRc = setpriority(PRIO_PROCESS, 0, nNewPriority);
	// TODO N Make the Scheduler selection a parameter.
	nRc = sched_setscheduler(nPid, SCHED_FIFO, &stSchedulerParm);
#endif
	if (nRc < 0)
		perror("ERROR setting RT scheduling class: ");
	return (nRc);
} /* end SetMaxRtPriorit */
